package com.jsm.tf.http;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.jsm.tf.exception.BusinessException;
import com.jsm.tf.request.RequestMethod;
import com.jsm.tf.request.TfRequest;
import com.jsm.tf.response.TfResponse;
import com.jsm.tf.view.dialog.TFDialog;
import de.felixroske.jfxsupport.GUIState;
import okhttp3.*;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

import java.io.IOException;
import java.util.*;
import java.util.concurrent.TimeUnit;

@Component
public class HttpClient {
    private static final Set<String> IGNORE_FIELD = new HashSet();
    private static final Set<Class> SUPPORT_PARAM_CLASS = new HashSet();

    static {
        IGNORE_FIELD.add("responseClass");
        IGNORE_FIELD.add("uri");
        IGNORE_FIELD.add("method");
        SUPPORT_PARAM_CLASS.add(Byte.TYPE);
        SUPPORT_PARAM_CLASS.add(Short.TYPE);
        SUPPORT_PARAM_CLASS.add(Integer.TYPE);
        SUPPORT_PARAM_CLASS.add(Long.TYPE);
        SUPPORT_PARAM_CLASS.add(Double.TYPE);
        SUPPORT_PARAM_CLASS.add(Float.TYPE);
        SUPPORT_PARAM_CLASS.add(Boolean.TYPE);
        SUPPORT_PARAM_CLASS.add(Character.TYPE);
        SUPPORT_PARAM_CLASS.add(Byte.class);
        SUPPORT_PARAM_CLASS.add(Short.class);
        SUPPORT_PARAM_CLASS.add(Integer.class);
        SUPPORT_PARAM_CLASS.add(Long.class);
        SUPPORT_PARAM_CLASS.add(Double.class);
        SUPPORT_PARAM_CLASS.add(Float.class);
        SUPPORT_PARAM_CLASS.add(Boolean.class);
        SUPPORT_PARAM_CLASS.add(Character.class);
        SUPPORT_PARAM_CLASS.add(String.class);
    }

    private final OkHttpClient httpClient = new OkHttpClient.Builder()
            .connectTimeout(20, TimeUnit.SECONDS)
            .build();
    private final MediaType JSON_MEDIA_TYPE = MediaType.parse("application/json; charset=utf-8");
    @Value("${tf.server.http.host:127.0.0.1}")
    private String host;
    @Value("${tf.server.http.protocol:http}")
    private String protocol;
    @Value("${tf.server.http.port:8080}")
    private int port;

    private static String toStringValue(Object v) {
        if (v == null) {
            return null;
        } else if (!SUPPORT_PARAM_CLASS.contains(v.getClass())) {
            throw new RuntimeException("do not support param type " + v.getClass().getName());
        } else {
            return v.toString();
        }
    }

    private static Map<String, String> parseTfRequest(TfRequest tfRequest) {
        if (tfRequest == null) {
            throw new NullPointerException("param " + TfRequest.class.getName() + " cannot be null");
        } else {
            Map<String, String> paramMap = new LinkedHashMap(16);
            JSONObject params = (JSONObject) JSON.toJSON(tfRequest);
            Iterator var3 = params.entrySet().iterator();

            while (true) {
                Map.Entry entry;
                do {
                    do {
                        do {
                            do {
                                if (!var3.hasNext()) {
                                    return paramMap;
                                }

                                entry = (Map.Entry) var3.next();
                            } while (entry == null);
                        } while (entry.getKey() == null);
                    } while (entry.getValue() == null);
                } while (IGNORE_FIELD.contains(entry.getKey()));

                String key = (String) entry.getKey();
                Object value = entry.getValue();
                paramMap.put(key, toStringValue(value));
            }
        }
    }

    public <T extends TfResponse> T request(TfRequest<T> request) {
        if (request == null) {
            return null;
        }
        HttpUrl.Builder urlBuilder = HttpUrl.parse(String.format("%s://%s:%s/%s", protocol, host, port, request.getUri()))
                .newBuilder();
        Map<String, String> paramMap = parseTfRequest(request);
        if (RequestMethod.Method_POST.equals(request.getMethod())) {

            RequestBody body = FormBody.create(JSON_MEDIA_TYPE, JSON.toJSONString(paramMap));
            Request postRequest = new Request.Builder()
                    .url(urlBuilder.build())
                    .post(body)
                    .build();
            try {
                Response response = httpClient.newCall(postRequest).execute();
                return adepterResponse(response, request.getResponseClass());
            } catch (Exception e) {
                throw new BusinessException("接口调用失败，请确认服务端网络可达性", "服务端异常");
            }
        } else {
            try {
                paramMap.forEach((key, value) -> {
                    urlBuilder.addQueryParameter(key, value);
                });
                Request getRequest = new Request.Builder()
                        .url(urlBuilder.build())
                        .get()
                        .build();
                Response response = httpClient.newCall(getRequest).execute();
                T t = adepterResponse(response, request.getResponseClass());
                delTfResponse(t);
                return t;
            } catch (Exception e) {
                throw new BusinessException("接口调用失败，请确认服务端网络可达性", "服务端异常");
            }
        }

    }

    public void delTfResponse(TfResponse response) {
        if (!response.isSuccess()) {
            TFDialog.showMessageDialog(GUIState.getStage(), response.getErrorMsg(), "业务异常");
        }
    }

    private <T extends TfResponse> T adepterResponse(Response response, Class<T> responseClass) throws IOException {
        if (response.isSuccessful()) {
            return JSON.parseObject(response.body().string(), responseClass);
        }
        return null;
    }

}
